package co.edu.usbcali.ai.ga.bl;

import java.util.ArrayList;
import java.util.List;

import com.cgarias.utils.Random;

import co.edu.usbcali.ai.ga.Ga;
import co.edu.usbcali.ai.ga.Generation;
import co.edu.usbcali.ai.ga.ICromosom;
import co.edu.usbcali.ai.ga.Individual;

public class DiscretUniformCrosser implements ICrosser {
	
	private static DiscretUniformCrosser instance=new DiscretUniformCrosser();

	int cutType=VARIABLE_MULTIPLE_CUT;
	// 0 significa sin limite de cortes
	int cuts=0;
	int maxRetries=20;
	
	/* **********************************************
	 * CONSTRUCTORS
	 * **********************************************/

	/**
	 * @param cutType
	 * @param cuts
	 * @param maxRetries
	 */
	public DiscretUniformCrosser(int cutType, int cuts, int maxRetries) {
		this.cutType = cutType;
		this.cuts = cuts;
		this.maxRetries = maxRetries;
	}

	public DiscretUniformCrosser() {
	}

	/* **********************************************
	 * 
	 * **********************************************/


	@SuppressWarnings("unchecked")
	@Override
	public List<ICromosom> cross(ICromosom father, ICromosom mother,Class<? extends ICromosom> cromosomImplementation) throws InstantiationException, IllegalAccessException {
		List<ICromosom> list=new ArrayList<ICromosom>();
		// Se maneja una lista de cortes porque el método puede usar multiples cortes 
		// (aunque normalmente solo es uno)
		int size=mother.size();
		ICromosom child1=cromosomImplementation.newInstance();
		ICromosom child2=cromosomImplementation.newInstance();
		child1.setType(father.getType());
		child2.setType(father.getType());
		for(int i=0;i<size;i++){
			if(Math.random()>=0.5){
				child1.add(father.get(i));
				child2.add(mother.get(i));
			}else{
				child1.add(mother.get(i));
				child2.add(father.get(i));
			}
		}
		list.add(child1);
		list.add(child2);
		return list;
	}


	public static ICrosser getInstance() {
		return instance;
	}

	@Override
	public List<Individual> cross(List<Individual> list, int children,
			int generationNumber,Class<? extends ICromosom> cromosomImplementation,Ga ga) throws InstantiationException, IllegalAccessException {
		List<ICromosom> cromosomsParentsList=Generation.getCromosomsList(list);
		List<ICromosom> cromosomsList=new ArrayList<ICromosom>(children);
		ICromosom father;
		ICromosom mother;
		for (int con=0;con<children;con+=2){
			int retries=0;
			List<ICromosom> cList;
			do{
				int fatherIndex=Random.random(list.size());
				father=cromosomsParentsList.get(fatherIndex);
				int motherIndex=Random.random(list.size());
				int ret=0;
				do{
					motherIndex=(motherIndex+1)%list.size();
					mother=cromosomsParentsList.get(motherIndex);
					System.out.println("Retries mother:"+ret+","+mother+","+motherIndex);
					if (ret==list.size()){
						System.out.println("father:"+father+","+fatherIndex);
						retries=maxRetries;
						break;
//						System.exit(-100);
					}
					ret++;
//				}while (motherIndex==fatherIndex);					
				}while (mother.diferentGenes(father)<2);
				cList=cross(father,mother,cromosomImplementation);
//				cList=mother.crossWith(father, cutType);
				System.out.println("Retries:"+retries);
				retries++;
			}while ((cromosomsParentsList.contains(cList.get(0))||cromosomsParentsList.contains(cList.get(1))||
				cromosomsList.contains(cList.get(0))||cromosomsList.contains(cList.get(1)))&&(retries<maxRetries));
			if (retries>=maxRetries){
				System.out.print("Max retries reached");
				IMutator mutator=SimpleMutator.getInstance();
				ICromosom child0=cList.get(0);
				ICromosom child1=cList.get(1);
				while(cromosomsParentsList.contains(child0)||cromosomsList.contains(child0)){
					child0=mutator.mutate(child0,cromosomImplementation);
				}
				while(cromosomsParentsList.contains(child1)||cromosomsList.contains(child1)){
					child1=mutator.mutate(child1,cromosomImplementation);
				}
				cList.clear();
				cList.add(child0);
				cList.add(child1);
			}
			cromosomsList.addAll(cList);
		}
		return Generation.createIndividualsList(cromosomsList,generationNumber,ga);

	}

	/* **********************************************
	 * GETTERS AND SETTERS
	 * **********************************************/

	public int getCutType() {
		return cutType;
	}

	public void setCutType(int cutType) {
		this.cutType = cutType;
	}

	public int getCuts() {
		return cuts;
	}

	public void setCuts(int cuts) {
		this.cuts = cuts;
	}

	public int getMaxRetries() {
		return maxRetries;
	}

	public void setMaxRetries(int maxRetries) {
		this.maxRetries = maxRetries;
	}	
	

	
}
